<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>学习事件模型</title>
</head>
<body>
  <script>
    var events = (function () {
      eventsObj = {}
      return {
        /** 注册事件, 可以连续注册, 可以注册多个事件 */
        on (type, handle) {
          (eventsObj[type] || (eventsObj[type] = [] )).push(handle)
        },
        /** 移除事件, 
         * * 如果没有参数, 移除所有事件, 
         * * 如果只带有 事件名 参数, 就移除这个事件名下的所有事件,
         * * 如果带有 两个 参数, 那么就是表示移除某一个事件的具体处理函数
         * */
        off (type, handle) {
          if (arguments.length === 0) {// 没有参数移除所有的事件
            eventsObj = {}
          } else if (arguments.length === 1) {
            eventsObj[type] = []
          } else if (arguments.length === 2) {
            let _events = eventsObj[type]
            if (!_events) return
            // 倒着循环 数组的 序号不会受到影响
            for(let i = _events.length; i > 0 ; i--) {
              if (_events[i] === handle) {
                _events.splice(i, 1)
              }
            }
          }
        },
        /** 
         * 发射事件, 触发事件, 包装参数 传递给事件处理函数
        */
       emit (type) {
        let args = Array.prototype.slice.call( arguments, 1 ) // 获得arguments 从 1 开始后的所有参数, 返回的是数组
        let _events = eventObjs[ type ]
        if ( !_events ) return

        for ( let i = 0; i < _events.length; i++ ) {
          // 如果要绑定上下文就需要使用 call 或 apply
          _events[ i ].apply( null, args ) // 第一个参数是上下文，第二个是参数
        }
       }
      }
    }())
  </script>
</body>
</html>